ViewPager i fragmenty-jaki jest właściwy sposób przechowywania stanu fragmentu?

Fragmenty wydają się być bardzo miłe dla oddzielenia logiki interfejsu użytkownika na niektóre moduły. Ale wraz z ViewPager jego cykl życia jest nadal mglisty dla mnie. Więc myśli Guru są bardzo potrzebne!

Edit

Zobacz głupie rozwiązanie poniżej ; -)

Zakres

Główna aktywność ma {[5] } z fragmentami. Fragmenty te mogą zaimplementować nieco inną logikę dla innych (podmenu) działań, więc dane fragmentów są wypełniane za pomocą interfejsu zwrotnego wewnątrz aktywności. I wszystko działa dobrze na pierwszym uruchomieniu, ale!...

Problem

Kiedy aktywność zostanie odtworzona (np. przy zmianie orientacji), tak samo robią fragmenty ViewPager. Kod (znajdziesz poniżej) mówi, że przy każdym tworzeniu aktywności staram się utworzyć nowy adapter ViewPager fragments taki sam jak fragments (może to jest problem), ale FragmentManager ma już wszystkie te fragmenty zapisane gdzieś (gdzie?) i uruchamia dla nich mechanizm rekreacyjny. Czyli mechanizm rekreacyjny wywołuje "Stary" fragment onAttach, onCreateView, itp. z moim wywołaniem zwrotnym do inicjowania danych za pomocą zaimplementowanej metody działania. Ale ta metoda wskazuje na nowo utworzony fragment, który jest tworzony za pomocą metody aktywności onCreate.

Wydanie

Może używam niewłaściwych wzorców, ale nawet Android 3 Pro book nie ma o tym wiele. Więc, proszę , daj mi jeden do dwóch uderzeń i wskaż, jak zrobić to we właściwy sposób. Wiele dzięki!

Kod

Główna Działalność

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePagerActivity aka helper

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

Adapter

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

Fragment

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from host acitivity, etc... */
}

Rozwiązanie

Głupim rozwiązaniem jest zapisanie fragmentów wewnątrz onSaveInstanceState (aktywności hosta) za pomocą putFragment i umieszczenie ich wewnątrz onCreate za pomocą getFragment. Ale nadal mam dziwne uczucie, że rzeczy nie powinny tak działać... Zob. kod poniżej:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}
Author: Impredicative, 2011-10-31

11 answers

Kiedy FragmentPagerAdapter dodaje fragment do Menedżera FragmentManager, używa specjalnego znacznika opartego na konkretnej pozycji, w której fragment zostanie umieszczony. FragmentPagerAdapter.getItem(int position) jest wywoływana tylko wtedy, gdy fragment dla tej pozycji nie istnieje. Po obróceniu Android zauważy, że już utworzył / zapisał fragment dla tej konkretnej pozycji, więc po prostu próbuje połączyć się z nim za pomocą FragmentManager.findFragmentByTag(), zamiast tworzyć nowy. Wszystko to jest darmowe, gdy używasz FragmentPagerAdapter i dlatego zwykle masz swój fragment kodu inicjalizacji wewnątrz metody getItem(int).

Nawet gdybyśmy nie używali FragmentPagerAdapter, nie jest dobrym pomysłem tworzenie nowego fragmentu za każdym razem w Activity.onCreate(Bundle). Jak zauważyłeś, gdy fragment zostanie dodany do Fragmentmanagera, zostanie odtworzony po obróceniu i nie ma potrzeby dodawania go ponownie. Jest to częsta przyczyna błędów podczas pracy z fragmentami.

Zwykłe podejście podczas pracy z fragmentami jest takie:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

Podczas używania FragmentPagerAdapter, rezygnujemy z zarządzania fragmentami na rzecz adaptera i nie musimy wykonywać powyższych kroków. Domyślnie ładuje tylko jeden Fragment przed i za bieżącą pozycją (chociaż nie niszczy ich, chyba że używasz FragmentStatePagerAdapter). To jest kontrolowane przez ViewPager.setOffscreenPageLimit (int) . Z tego powodu bezpośrednie wywołanie metod na fragmentach poza adapterem nie jest gwarantowane, ponieważ mogą nawet nie być żywe.

Do cięcia a krótko mówiąc, Twoje rozwiązanie, aby użyć putFragment, aby móc uzyskać odniesienie później, nie jest tak szalone, i nie tak w przeciwieństwie do normalnego sposobu korzystania z fragmentów (powyżej). Trudno jest uzyskać odniesienie w inny sposób, ponieważ fragment jest dodawany przez adapter, a nie osobiście. Po prostu upewnij się, że offscreenPageLimit jest wystarczająco wysoka, aby załadować pożądane fragmenty przez cały czas, ponieważ polegasz na tym, że są obecne. To omija leniwe możliwości ładowania Viewpagera, ale wydaje się być tym, co Ty chęć zgłoszenia.

Innym podejściem jest nadpisanie FragmentPageAdapter.instantiateItem(View, int) i zapisanie odwołania do fragmentu zwróconego z super call przed jego zwróceniem(ma logikę znajdowania fragmentu, jeśli już istnieje).

Aby uzyskać pełniejszy obraz, spójrz na niektóre źródła FragmentPagerAdapter (krótki) i ViewPager (długi).

 454
Author: antonyt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-28 17:32:41

Chcę zaoferować rozwiązanie, które rozszerza się na antonyt's wspaniała odpowiedź i wspomnieć o nadpisaniu FragmentPageAdapter.instantiateItem(View, int), Aby zapisać odniesienia do utworzonych Fragments, abyś mógł pracować nad nimi później. Powinno to również działać z FragmentStatePagerAdapter; Zobacz uwagi po szczegóły.


Oto prosty przykład, jak uzyskać odniesienie do Fragments zwracanego przez FragmentPagerAdapter, które nie opiera się na wewnętrznym tags ustawionym na Fragments. Kluczem jest nadpisanie instantiateItem() i zapisz referencje w tam zamiast W getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

        public CustomPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

Lub jeśli wolisz pracować z tags zamiast zmiennych klasy / odniesień do Fragments Możesz również pobrać tags ustawione przez FragmentPagerAdapter w ten sam sposób: UWAGA: Nie dotyczy to FragmentStatePagerAdapter, ponieważ nie ustawia tags podczas tworzenia swojego Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

Zauważ, że ta metoda nie polega na naśladowaniu wewnętrznego tag ustawionego przez FragmentPagerAdapter i zamiast tego używa odpowiednich API do ich pobierania. W ten sposób nawet jeśli tag zmiany w przyszłych wersjach SupportLibrary nadal będziesz bezpieczny.


Nie zapomnij, że w zależności od projektu Activity, Fragments, nad którym próbujesz pracować, może jeszcze istnieć, więc musisz to uwzględnić, wykonując null Kontrole Przed użyciem referencji.

Ponadto, jeśli zamiast pracujesz z FragmentStatePagerAdapter, to nie chcesz zachować twardych odniesień do swojego Fragments, ponieważ możesz mieć ich wiele i twarde odniesienia byłyby niepotrzebnie zachować je w pamięci. Zamiast standardowych zapisywać odwołania Fragment w zmiennych WeakReference. Tak:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}
 38
Author: Tony Chan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:33

Znalazłem inne stosunkowo łatwe rozwiązanie twojego pytania.

Jak widać z kodu źródłowego FragmentPagerAdapter, fragmenty zarządzane przez FragmentPagerAdapter przechowują się w FragmentManager pod tagiem wygenerowanym przy użyciu:

String tag="android:switcher:" + viewId + ":" + index;

viewId jest container.getId(), container jest twoją ViewPager instancją. index jest pozycją fragmentu. Stąd można zapisać ID obiektu do outState:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

Jeśli chcesz się komunikować z tym fragmentem, możesz uzyskać if z FragmentManager, np.:

getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewpagerid + ":0")
 19
Author: user2110066,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 17:06:06

Chcę zaproponować alternatywne rozwiązanie dla Być może nieco innej sprawy, ponieważ wiele moich poszukiwań odpowiedzi prowadziło mnie do tego wątku.

Moja sprawa - Tworzę / dodaję strony dynamicznie i przesuwam je do Viewpagera, ale po obróceniu (onConfigurationChange) kończę z nową stroną, ponieważ oczywiście OnCreate jest wywoływany ponownie. Ale chcę zachować odniesienia do wszystkich stron, które zostały utworzone przed rotacją.

Problem - I nie mam unikalnych identyfikatorów dla każdego tworzonego przeze mnie fragmentu, więc jedynym sposobem na odwołanie się było przechowywanie referencji w tablicy, która ma zostać przywrócona po zmianie rotacji/konfiguracji.

Workaround - Kluczową koncepcją było, aby aktywność (która wyświetla fragmenty) zarządzała również tablicą odniesień do istniejących fragmentów, ponieważ ta aktywność może wykorzystywać wiązki w onSaveInstanceState {11]}

public class MainActivity extends FragmentActivity

Więc w ramach tej działalności, oświadczam, że członek prywatny do śledź otwarte strony

private List<Fragment> retainedPages = new ArrayList<Fragment>();
To jest aktualizowane za każdym razem, gdy onSaveInstanceState jest wywoływany i przywracany w onCreate
@Override
protected void onSaveInstanceState(Bundle outState) {
    retainedPages = _adapter.exportList();
    outState.putSerializable("retainedPages", (Serializable) retainedPages);
    super.onSaveInstanceState(outState);
}

...więc po przechowywaniu można go odzyskać...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
    }
    _mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
    _adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
    if (retainedPages.size() > 0) {
        _adapter.importList(retainedPages);
    }
    _mViewPager.setAdapter(_adapter);
    _mViewPager.setCurrentItem(_adapter.getCount()-1);
}

Były to niezbędne zmiany w głównej działalności, więc potrzebowałem członków i metod w moim FragmentPagerAdapter, aby to działało, więc w {11]}

public class ViewPagerAdapter extends FragmentPagerAdapter

Identyczna konstrukcja (jak pokazano powyżej w MainActivity)

private List<Fragment> _pages = new ArrayList<Fragment>();

I to synchronizowanie (jak wyżej w onSaveInstanceState) jest wspierany w szczególności przez metody

public List<Fragment> exportList() {
    return _pages;
}

public void importList(List<Fragment> savedPages) {
    _pages = savedPages;
}

I wreszcie, w klasie fragment

public class CustomFragment extends Fragment

Aby to wszystko zadziałało, nastąpiły dwie zmiany, pierwsza

public class CustomFragment extends Fragment implements Serializable

A następnie dodanie tego do onCreate, aby fragmenty nie zostały zniszczone

setRetainInstance(true);

Wciąż jestem w trakcie owijania głowy wokół fragmentów i cyklu życia Androida, więc zastrzeżenie jest tutaj, że mogą być redundancje/nieefektywności w tej metodzie. Ale to działa dla mnie i ja nadzieja może być pomocna dla innych z przypadkami podobnymi do moich.

 17
Author: Frank Yin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-04 04:12:19

Moje rozwiązanie jest bardzo niegrzeczne, ale działa: będąc moimi fragmentami dynamicznie tworzonymi z zachowanych danych, po prostu usuwam cały fragment z PageAdapter przed wywołaniem super.onSaveInstanceState(), a następnie odtwarzam je przy tworzeniu aktywności:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
    mSectionsPagerAdapter.removeAllfragments();
    super.onSaveInstanceState(outState);
}

Nie możesz ich usunąć w onDestroy(), w przeciwnym razie otrzymasz wyjątek:

java.lang.IllegalStateException: nie można wykonać tej czynności po onSaveInstanceState

Tutaj kod w adapterze strony:

public void removeAllfragments()
{
    if ( mFragmentList != null ) {
        for ( Fragment fragment : mFragmentList ) {
            mFm.beginTransaction().remove(fragment).commit();
        }
        mFragmentList.clear();
        notifyDataSetChanged();
    }
}

Zapisuję tylko bieżącą stronę i przywracam ją w onCreate(), po fragmentach zostały stworzone.

if (savedInstanceState != null)
    mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );  
 8
Author: Sir John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 10:09:23

Co to jest BasePagerAdapter? Powinieneś użyć jednego ze standardowych adapterów pager -- FragmentPagerAdapter lub FragmentStatePagerAdapter, w zależności od tego, czy chcesz, aby fragmenty, które nie są już potrzebne przez ViewPager, były przechowywane w pobliżu (pierwszy) lub aby ich stan został zapisany (drugi) i w razie potrzeby ponownie utworzony.

Przykładowy kod do użycia ViewPager można znaleźć tutaj

To prawda, że zarządzanie fragmentami w pagerze widoku między instancjami aktywności jest trochę skomplikowane, ponieważ FragmentManager w frameworku dba o zapisanie stanu i przywrócenie wszystkich aktywnych fragmentów, które pager wykonał. Wszystko to naprawdę oznacza, że adapter podczas inicjalizacji musi upewnić się, że ponownie łączy się z odtworzonymi fragmentami. Możesz spojrzeć na kod FragmentPagerAdapter lub FragmentStatePagerAdapter, aby zobaczyć, jak to się robi.

 4
Author: hackbod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 17:08:35

Jeśli ktoś ma problemy ze swoim fragmentem Statepageradapter nie przywracając prawidłowo stanu jego fragmentów...tzn....nowe fragmenty są tworzone przez FragmentStatePagerAdapter zamiast przywracać je ze stanu...

Upewnij się, że zadzwonisz ViewPager.setOffscreenPageLimit() zanim zadzwonisz ViewPager.setAdapter(fragmentStatePagerAdapter)

Po wywołaniu ViewPager.setOffscreenPageLimit()...ViewPager natychmiast spojrzy na swój adapter i spróbuje pobrać jego fragmenty. Może się to zdarzyć, zanim ViewPager będzie miał szansę przywrócić fragmenty z savedInstanceState (tworzenie nowych fragmentów, których nie można ponownie zainicjować z SavedInstanceState, ponieważ są nowe).

 2
Author: dell116,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-25 07:45:33

Wymyśliłam proste i eleganckie rozwiązanie. Zakłada ona, że aktywność jest odpowiedzialna za tworzenie fragmentów, A Adapter po prostu im służy.

To jest kod adaptera (nic dziwnego, poza faktem, że mFragments jest listą fragmentów utrzymywanych przez działanie)

class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        TabFragment fragment = (TabFragment)mFragments.get(position);
        return fragment.getTitle();
    }
} 

Cały problem tego wątku polega na uzyskaniu odniesienia do "starych" fragmentów, więc używam tego kodu w aktywności onCreate.

    if (savedInstanceState!=null) {
        if (getSupportFragmentManager().getFragments()!=null) {
            for (Fragment fragment : getSupportFragmentManager().getFragments()) {
                mFragments.add(fragment);
            }
        }
    }

Oczywiście możesz dodatkowo dostroić ten kod w razie potrzeby, na przykład upewniając się, że fragmenty są instancjami danej klasy.

 0
Author: Merlevede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-19 16:31:30

Aby uzyskać fragmenty po zmianie orientacji, musisz użyć .getTag ().

    getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)

Dla nieco większej obsługi napisałem własną ArrayList dla mojej PageAdapter, aby pobrać fragment przez viewPagerId i FragmentClass w dowolnej pozycji:

public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";

private ArrayList<MyPageBuilder> fragmentPages;

public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
    super(fm);
    fragmentPages = fragments;
}

@Override
public Fragment getItem(int position) {
    return this.fragmentPages.get(position).getFragment();
}

@Override
public CharSequence getPageTitle(int position) {
    return this.fragmentPages.get(position).getPageTitle();
}

@Override
public int getCount() {
    return this.fragmentPages.size();
}


public int getItemPosition(Object object) {
    //benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden

    Log.d(logTAG, object.getClass().getName());
    return POSITION_NONE;
}

public Fragment getFragment(int position) {
    return getItem(position);
}

public String getTag(int position, int viewPagerId) {
    //getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())

    return "android:switcher:" + viewPagerId + ":" + position;
}

public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
    return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}


public static class MyPageBuilder {

    private Fragment fragment;

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    private String pageTitle;

    public String getPageTitle() {
        return pageTitle;
    }

    public void setPageTitle(String pageTitle) {
        this.pageTitle = pageTitle;
    }

    private int icon;

    public int getIconUnselected() {
        return icon;
    }

    public void setIconUnselected(int iconUnselected) {
        this.icon = iconUnselected;
    }

    private int iconSelected;

    public int getIconSelected() {
        return iconSelected;
    }

    public void setIconSelected(int iconSelected) {
        this.iconSelected = iconSelected;
    }

    public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
        this.pageTitle = pageTitle;
        this.icon = icon;
        this.iconSelected = selectedIcon;
        this.fragment = frag;
    }
}

public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
    private final String logTAG = MyPageArrayList.class.getName() + ".";

    public MyPageBuilder get(Class cls) {
        // Fragment über FragmentClass holen
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return super.get(indexOf(item));
            }
        }
        return null;
    }

    public String getTag(int viewPagerId, Class cls) {
        // Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return "android:switcher:" + viewPagerId + ":" + indexOf(item);
            }
        }
        return null;
    }
}

Więc po prostu utwórz MyPageArrayList z fragmentami:

    myFragPages = new MyPageAdapter.MyPageArrayList();

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_data_frag),
            R.drawable.ic_sd_storage_24dp,
            R.drawable.ic_sd_storage_selected_24dp,
            new WidgetDataFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_color_frag),
            R.drawable.ic_color_24dp,
            R.drawable.ic_color_selected_24dp,
            new WidgetColorFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_textsize_frag),
            R.drawable.ic_settings_widget_24dp,
            R.drawable.ic_settings_selected_24dp,
            new WidgetTextSizeFrag()));

I dodaj je do viewpagera:

    mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
    myViewPager.setAdapter(mAdapter);

Po tym można uzyskać po zmianie orientacji poprawny fragment za pomocą jego klasy:

        WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
            .findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));
 0
Author: Scrounger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-28 23:47:26

Nieco inna opinia zamiast samodzielnie przechowywać fragmenty zostaw ją w Fragmentmanagerze, a gdy musisz coś zrobić z fragmentami, poszukaj ich w Fragmentmanagerze:

//make sure you have the right FragmentManager 
//getSupportFragmentManager or getChildFragmentManager depending on what you are using to manage this stack of fragments
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null) {
   int count = fragments.size();
   for (int x = 0; x < count; x++) {
       Fragment fragment = fragments.get(x);
       //check if this is the fragment we want, 
       //it may be some other inspection, tag etc.
       if (fragment instanceof MyFragment) {
           //do whatever we need to do with it
       }
   }
}

Jeśli masz dużo fragmentów i koszt instanceof check może nie być tym, czego chcesz, ale dobrze jest mieć na uwadze, że FragmentManager już uwzględnia fragmenty.

 0
Author: etodorov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-05 12:12:02

Dodaj:

   @SuppressLint("ValidFragment")
Przed zajęciami.

To nie działa zrób coś takiego:

@SuppressLint({ "ValidFragment", "HandlerLeak" })
 -31
Author: andro_stackoverflow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-17 20:58:07